package cn.leida.ruoyi.system.service.impl;

import cn.leida.ruoyi.common.constant.UserConstants;
import cn.leida.ruoyi.common.core.domain.TreeSelect;
import cn.leida.ruoyi.common.core.domain.entity.SysMenu;
import cn.leida.ruoyi.common.core.domain.entity.SysRole;
import cn.leida.ruoyi.common.core.domain.entity.SysUser;
import cn.leida.ruoyi.common.utils.SecurityUtils;
import cn.leida.ruoyi.common.utils.code.BusinessBizCode;
import cn.leida.ruoyi.system.dao.SysRoleMenuDao;
import cn.leida.ruoyi.system.dto.RouterVo;
import cn.leida.ruoyi.system.dao.SysMenuDao;
import cn.leida.ruoyi.system.dao.SysRoleDao;
import cn.leida.ruoyi.system.service.ISysMenuService;
import cn.leida.ruoyi.system.util.SysJsonUtil;
import cn.leida.ruoyi.system.util.SysMenuUtil;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import cn.leida.ruoyi.common.utils.DateUtils;
import cn.leida.ruoyi.common.utils.StringUtils;
import cn.leida.ruoyi.common.core.text.Convert;

/**
 * 菜单 业务层处理
 *
 * @author liutietou
 * @since 1.0  2020-12-12
 */
@Transactional(readOnly = true)
@Service
public class SysMenuServiceImpl implements ISysMenuService {

    public static final String PREMISSION_STRING = "perms[\"{0}\"]";

    @Autowired
    private SysMenuDao menuDao;

    @Autowired
    private SysRoleDao roleDao;

    @Autowired
    private SysRoleMenuDao roleMenuDao;

    /**
     * 根据用户查询系统菜单列表
     *
     * @param userId 用户ID
     * @return 菜单列表
     */
    @Override
    public List<SysMenu> selectMenuList(Long userId) {
        return selectMenuList(new SysMenu(), userId);
    }

    @Override
    public List<SysMenu> findRoleMenuList(Long roleId) {
        return menuDao.findRoleMenuList(roleId);
    }

    /**
     * 查询系统菜单列表
     *
     * @param menu 菜单信息
     * @return 菜单列表
     */
    @Override
    public List<SysMenu> selectMenuList(SysMenu menu, Long userId) {
        if (SysUser.isAdmin(userId)) {
            return selectMenuList(menu);
        } else {
            return SysJsonUtil.toSysMenuList(menuDao.queryUserMenuList(userId, "0", "0"));
        }
    }

    @Override
    public List<SysMenu> selectMenuList(SysMenu menu) {
        // 查询结果按照id倒序
        Sort sort = Sort.by(Sort.Order.asc("orderNum"));
        Specification<SysMenu> example = new Specification<SysMenu>() {
            private static final long serialVersionUID = 1L;

            @Override
            public Predicate toPredicate(Root<SysMenu> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> list = new ArrayList<>();

                  /*  if (null != menu.getMenuId()) {
                        Predicate pre = cb.greaterThanOrEqualTo(root.get("menuId").as(Long.class), ClientMenuUtil.clientMenuMiniId());
                        list.add(pre);
                    }*/
                if (StringUtils.isNoneBlank(menu.getMenuName())) {
                    Predicate pre = cb.like(root.get("menuName").as(String.class), "%" + menu.getMenuName() + "%");
                    list.add(pre);
                }
                if (StringUtils.isNoneBlank(menu.getVisible())) {
                    Predicate pre = cb.equal(root.get("visible").as(String.class), menu.getVisible());
                    list.add(pre);
                }
                if (StringUtils.isNoneBlank(menu.getStatus())) {
                    Predicate pre = cb.like(root.get("status").as(String.class), menu.getStatus());
                    list.add(pre);
                }
                if (list.isEmpty()) {
                    return null;
                }
                return cb.and(list.toArray(new Predicate[0]));
            }
        };
        return menuDao.findAll(example, sort);

    }

    /**
     * 根据用户ID查询权限
     *
     * @param userId 用户ID
     * @return 权限列表
     */
    @Override
    public Set<String> selectMenuPermsByUserId(Long userId) {
        List<String> perms = menuDao.findMenuPermsByUserId(userId);
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms) {
            if (StringUtils.isNotEmpty(perm)) {
                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
            }
        }
        return permsSet;
    }

    /**
     * 根据用户ID查询菜单
     *
     * @param userId 用户名称
     * @return 菜单列表
     */
    @Override
    public List<SysMenu> selectMenuTreeByUserId(Long userId) {
        List<SysMenu> menus;
        if (SecurityUtils.isAdmin(userId)) {
            menus = SysJsonUtil.toSysMenuList(menuDao.findMenuTreeAllList());
        } else {
            menus = SysJsonUtil.toSysMenuList(menuDao.findMenuTreeByUserId(userId));
        }
        return SysMenuUtil.getChildPerms(menus, 0);
    }

    /**
     * 根据角色ID查询菜单树信息
     *
     * @param roleId 角色ID
     * @return 选中菜单列表
     */
    @Override
    public List<Long> selectMenuListByRoleId(Long roleId) {
        SysRole role = roleDao.findByRoleId(roleId);
        //  菜单树选择项是否关联显示
        if (role.isMenuCheckStrictly()) {
            return menuDao.findMenuCheckListByRoleId(roleId);
        }
        return menuDao.findMenuListByRoleId(roleId);
    }

    /**
     * 构建前端路由所需要的菜单
     *
     * @param menus 菜单列表
     * @return 路由列表
     */
    @Override
    public List<RouterVo> buildMenus(List<SysMenu> menus) {
        return SysMenuUtil.buildMenus(menus);
    }

    /**
     * 构建前端所需要树结构
     *
     * @param menus 菜单列表
     * @return 树结构列表
     */
    @Override
    public List<SysMenu> buildMenuTree(List<SysMenu> menus) {
        return SysMenuUtil.buildMenuTree(menus);
    }

    /**
     * 构建前端所需要下拉树结构
     *
     * @param menus 菜单列表
     * @return 下拉树结构列表
     */
    @Override
    public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus) {
        List<SysMenu> menuTrees = buildMenuTree(menus);
        return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    /**
     * 根据菜单ID查询信息
     *
     * @param menuId 菜单ID
     * @return 菜单信息
     */
    @Override
    public SysMenu selectMenuById(Long menuId) {
        return menuDao.findById(menuId).orElse(new SysMenu());
    }

    /**
     * 是否存在菜单子节点
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    @Override
    public boolean hasChildByMenuId(Long menuId) {
        int result = menuDao.hasChildByMenuId(menuId);
        return result > 0 ? true : false;
    }

    /**
     * 查询菜单使用数量
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    @Override
    public boolean checkMenuExistRole(Long menuId) {
        int result = roleMenuDao.checkMenuExistRole(menuId);
        return result > 0 ? true : false;
    }

    /**
     * 新增保存菜单信息
     *
     * @param menu 菜单信息
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int insertMenu(SysMenu menu) {
        menuDao.save(menu);
        return BusinessBizCode.OPTION_SUCCESS.getCode();
    }

    /**
     * 修改保存菜单信息
     *
     * @param menu 菜单信息
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateMenu(SysMenu menu) {
        menuDao.save(menu);
        return BusinessBizCode.OPTION_SUCCESS.getCode();
    }

    /**
     * 删除菜单管理信息
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    @Override
    public int deleteMenuById(Long menuId) {
        menuDao.deleteById(menuId);
        return BusinessBizCode.OPTION_SUCCESS.getCode();
    }

    /**
     * 删除大于 某个ID的所有菜单
     *
     * @param minMenuId 最小值菜单
     * @return
     */
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    @Override
    public int deleteByMinMenuId(Long minMenuId) {
        menuDao.deleteByMinMenuId(minMenuId);
        return BusinessBizCode.OPTION_SUCCESS.getCode();
    }

    /**
     * 校验菜单名称是否唯一
     *
     * @param menu 菜单信息
     * @return 结果
     */
    @Override
    public boolean checkMenuNameUnique(SysMenu menu) {
        Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
        List<SysMenu> sysMenus = menuDao.findByMenuNameAndParentId(menu.getMenuName(), menu.getParentId());
        if (!sysMenus.isEmpty() && sysMenus.get(0).getMenuId().longValue() != menuId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    @Transactional(readOnly = false, rollbackFor = Exception.class)
    @Override
    public void saveChildren(SysMenu parentMenu) {
        parentMenu.setMenuId(null);
        // 保存menu: 并获取新的ID
        SysMenu newMenu = menuDao.save(parentMenu);
        List<SysMenu> children = parentMenu.getChildren();
        for (SysMenu child : children) {
            // 新ID
            child.setParentId(newMenu.getMenuId());
            // 递归调用
            saveChildren(child);
        }
    }

}
